This is an example to help demonstrate how to write DIRC macros. This macro automatically sets your Macintosh clock from a dialup time server.
This macro uses either the NIST (Boulder, CO) or Naval Observatory (Washington, DC) time server to determine the correct data and time for your Macintosh. In order to operate correctly, the 'map' control panel must have been used at some point to specify the location of your Macintosh. In addition, you must indicate whether you are on daylight savings time each time the macro is run.
*/
FUNC main()
{
INT timezone, result, dst2;
STR dialer = ":Macros:~AutoTime";
STR format, server, config, dst0, dst1;
/*
Make sure the dialer file is present before proceeding.
*/
IF (!is_file(dialer)) {
format = "The 'AutoTime Dialer' file is missing from^m"+
"the Macros folder making it impossible to^m"+
"use the AutoTime macro.";
WIN_NOTE(4,format);
END;
}
/*
Figure out which timezone the Macintosh is located in. It must be located within North America for it to work properly. The macro could be changed to work world-wide, but figuring out which time server to use gets tricky.
*/
timezone = (TIME(0) - TIME(1)) / 3600;
IF (timezone == 0) {
format = "You must use the 'Map' control panel to set^m"+
"the location and timezone of your Macintosh^m"+
"in order for this macro to operate.";
WIN_NOTE(4,format);
END;
}
IF ((timezone < -9) || (timezone > -4)) {
format = "Sorry, the AutoTime macro only operates within^m"+
"North America (timezones UTC-5 to UTC-9)";
WIN_NOTE(4,format);
END;
}
/*
Determine which time server to use depending on the timezone the Macintosh is located in. Use Naval Observatory for UTC-5 and NIST for everything else.
*/
server = (IF (timezone > -6) {
"(202) 653-0351";
} ELSE {
"(303) 494-4774";
})
UI_OPEN(dialer);
UI_SET("phone number",server);
UI_CLICK("Emulate");
config = UI_GET("Answerback");
/*
See if the macro has ever been used. If not, warn the user about the long distance phone call to the time server. Also get an initial daylight savings setting.
*/
IF (config == "") {
format = "WARNING! This macro places a long-distance phone^m"+
"call in order to get the current time to set your^m"+
"Macintosh clock. Do you want to continue?";
IF (WIN_NOTE(3,format) != 1) {
UI_CLOSE(dialer);
END;
}
format = "Are you currently on daylight savings time?^m"+
"(Usually between Apr and Oct in most areas.)";
result = WIN_NOTE(5,format));
IF (result == -1) { UI_CLOSE(dialer); END; }
config = STR_NUM(result);
UI_SET("Answerback",config);
UI_SAVE(dialer);
}
/*
Verify current settings and let user change daylight savings flag.
*/
WHILE (1) {
IF (config == "0") {
dst0 = "NO"; dst1 = "YES"; dst2 = 0;
} ELSE {
dst0 = "YES"; dst1 = "NO"; dst2 = 1;
}
format = "This macro will place long-distance call to^m"+
"%s to get the current time.^m"+
"Timezone: UTC%d, Daylight Savings: %s^M"+
"Is this all correct?";
result = WIN_NOTE(5,STR_FORMAT(format,server,timezone-dst2,dst0));
IF (result == -1) { UI_CLOSE(dialer); END; }
IF (result == 1) { BREAK };
format = "Change the setting of daylight savings^m"+
"from %s to %s?";
result = WIN_NOTE(5,STR_FORMAT(format,dst0,dst1));
IF (result == -1) { UI_CLOSE(dialer); END; }
IF (result == 0) {
format = "You need to use the 'Map' control panel^m"+
"to change your timezone. Do this and then^m"+
"restart the AutoTime macro.";
WIN_NOTE(4,format);
UI_CLOSE(dialer);
END;
}
config = IF (config == "0") { "1" } ELSE { "0" }
UI_SET("Answerback",config);
UI_SAVE(dialer);
}
/*
Everything is ready -- start the dialer running. The "connect:2" is needed because the macro needs to click the connect button, not the connect radio button (the :2 means the second occurence of "connect").
*/
UI_CLICK("Connect:2");
END;
}
/*
Use the output of the time server to set the Macintosh clock to the current time. Connecting to the time server provides a continuous output of time records. The two different time servers return records in different formats.
Need to deal with daylight savings. Setup a variable which indicates the current time offset due to daylight savings (expressed in seconds).
*/
config = SES_GET(0,"ANSWERBACK");
dst = IF (config == "1") { 3600 } ELSE { 0 };
/*
Wait for the time server to start sending data. Look for UTC because it appears in the data. Then wait for a couple seconds to process any data which may have been buffered by the modem during the initial connection.
*/
WT("UTC");
WT(200);
/*
Wait for two lines of input with identical lengths. This is an easy way to "get in sync" with the time server and get a complete line of time data.
*/
new_time = "";
DO {
old_time = new_time;
new_time = IN("^m");
IF (STR_LEN(new_time) < 5) { new_time = IN("^m"); }
} WHILE (STR_LEN(old_time) != STR_LEN(new_time));
/*
Because the time server may send linefeeds and other non-data at the
start/end of each line, it is important to make sure the beginning of the line contains normal data. The line should start with a digit.
*/
WHILE (STR_LEFT(new_time, 1) < "0") {
new_time = STR_MID(new_time, 1, 80);
}
/*
With the date/time field read, it is now possible to compute the offset between the Macintosh date and the real date. The Macintosh date is maintained as the number of elapsed seconds from Jan 1, 1904. The time server returns the date in modified julian day format. This means that date is expressed as the number of elapsed days from Nov 17, 1858 (actually it is really the number of elapsed days from Jan 1, 4713 B.C. mod 100000, but it is easier to think of it as the former). Converting from a Macintosh date to a julian date is really simple since all you need to do is convert from seconds to days (86400 seconds in a day). The "problem" is that the number which represents the Macintosh date (for any date past approx 1970) is larger than DIRC's largest positive integer. If you try and divide such a number, you get an invalid result. This can be avoided by subtracting 2398377600 (the number of elapsed seconds from Jan 1, 1904 to Jan 1, 1980) and making up the difference once the value is in julian format. Once converted to julian format, the offset between the Macintosh date and the time server date is determined which can later be used to "correct" the Macintosh clock.
The "trick" to getting a decent time estimate is to use the current server time to compute a "target" time. The "target" time is the time at which the macro will determine the offset between the Macintosh clock and the server clock. Once the offset between the two clocks is known, the rest of the macro is no longer "time critical". The target time is set 3 seconds later than the current time. This gives sufficient time for the macro to complete its computations and accurately synchronize the determination of the offset.
The NIST and Naval Observatory time servers return the time in different formats. Therefore, there are two different routine to extract the time depending on which server is being used. It is possible to determine the server type by looking at the length of the time record returned by the server. The NIST server returns more information (a larger record) than the Naval server.
The target time has now arrived (or at least is very close-- usually within 1 second which is good enough). Now figure out the offset by subtracting the server time from the Macintosh time. The 2147472000 is needed because the Macintosh time is expressed in elapsed seconds from January 1, 1904. This particular value is larger than the largest positive DIRC integer. In order to prevent an overflow, subtract 2398377600 from the time to turn it into a valid DIRC positive value. This works because the result is taken mod 86400 so as to get the offset relative to the current day. The resulting offset is in the range 0-86399. Values greater than 86400/2 are assumed to be negative and are changed to real negative values.
Time to disconnect from the time server. There is one little "glitch" at this point. If the WIN_NOTE() function executes before the disconnect completes, it will take precedence over the disconnect and essentially put the disconnect process on "hold". To avoid this, the macro waits for the window type to change from "TERM" (the online state) to "REVW" (the offline state).
*/
window = WIN_FIND(SES_GET(0,"Name"),"TERM");
WIN_ACTIVE(window);
UI_MENU("Online:Disconnect");
UI_CLICK("OK");
WHILE (WIN_TYPE(window) == "TERM") {
WT(100);
}
WIN_CLOSE(window);
/*
Check and make sure the macro was configured before it was run. If not, warn the user that the results may be bogus.
*/
IF (config == "") {
format = "WARNING! The AutoTime macro was run incorrectly.^m"+
"Exec the AutoTime Macro NOT the AutoTime Dialer^m"+
"in the future. As a result, the time adjustment^m"+
"may be incorrect.";
WIN_NOTE(4,format);
}
/*
At this point, the date and time offsets are known. Ask the user if they want to change the clock to correspond to the "corrected" date and/or time.